/**
    Copyright (C) 2012 Guido Ludwig

    This file is part of JATT.
	http://code.google.com/p/jatt/

    JATT is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package de.guidoludwig.jatt;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;

import org.jaudiotagger.tag.datatype.AbstractDataType;

import argparser.ArgParser;
import argparser.BooleanHolder;
import argparser.StringHolder;

import com.jgoodies.validation.Severity;
import com.jgoodies.validation.ValidationMessage;
import com.jgoodies.validation.ValidationResult;

import de.guidoludwig.jatt.chain.JATTChain;
import de.guidoludwig.jatt.chain.JATTContext;
import de.guidoludwig.jatt.util.CommandlineProgress;
import de.guidoludwig.jatt.util.DirectoryFilter;
import de.umass.lastfm.Caller;

/**
 * @author Guido Ludwig
 */
public class JATT {

	private static final String CMDLINE_CONFIG_INFO = "info";
	private static final String CMDLINE_CONFIG_DEFAULT = "default";
	private static final String CMDLINE_CONFIG_CLEAN = "clean";
	private static final String COPYRIGHT = "JATT Copyright (C) 2012 Guido Ludwig\n\n"
			+ "This program comes with ABSOLUTELY NO WARRANTY;\n"
			+ "This is free software, licenced under the GNU General Public Licence V.3\n"
			+ "http://www.gnu.org/licenses/gpl-3.0.en.html\n\n";

	public static final int LEVEL_DEBUG = 10;
	public static final int LEVEL_INFO = 5;

	private static int level = LEVEL_INFO;
	static {
		AbstractDataType.logger.setLevel(Level.SEVERE);
		Caller.getInstance().getLogger().setLevel(Level.SEVERE);
	}

	private final ValidationResult result;

	private int totalDirectories;
	private int currentDirectoryNumber;
	@SuppressWarnings("unused")
	private JATTConfig config;

	private CommandlineProgress progress;

	public JATT(int totalDirectories, JATTConfig config) {
		result = new ValidationResult();
		this.totalDirectories = totalDirectories;
		if (config == null) {
			System.out.println("Using configuration : " + null);
		} else {
			System.out.println("Using configuration : " + config.getName());
		}
		this.config = config;
		progress = new CommandlineProgress();

	}

	private void progress(String message) {
		currentDirectoryNumber++;
		int percentage = (currentDirectoryNumber * 100) / totalDirectories;
		progress.progress(percentage, message);
	}

	private void showResult() {
		progress.progress(100, "");
		System.out.println("");
		if (result.isEmpty()) {
			System.out.println("Empty Validation Result");
		} else {
			for (ValidationMessage validationMessage : result.getInfos()) {
				System.out.println(validationMessage.severity() + " "
						+ validationMessage.formattedText());
			}
			for (ValidationMessage validationMessage : result.getWarnings()) {
				System.out.println(validationMessage.severity() + " "
						+ validationMessage.formattedText());
			}
			for (ValidationMessage validationMessage : result.getErrors()) {
				System.out.println(validationMessage.severity() + " "
						+ validationMessage.formattedText());
			}
			System.out.println("\n\nTotal : \n" + Severity.ERROR + " "
					+ result.getErrors().size() + "\n" + Severity.WARNING + " "
					+ result.getWarnings().size());
		}
	}

	private void writeResult(File rootDirectory) throws IOException {
		File report = new File(rootDirectory, "jatt-report.txt");
		if (report.exists()) {
			report.delete();
		}
		FileWriter writer = new FileWriter(report);

		if (result.isEmpty()) {
			writer.write("Empty Validation Result");
		} else {
			for (ValidationMessage validationMessage : result.getInfos()) {
				writer.write(validationMessage.severity() + " "
						+ validationMessage.formattedText());
			}
			for (ValidationMessage validationMessage : result.getErrors()) {
				writer.write(validationMessage.severity() + " "
						+ validationMessage.formattedText());
			}
			for (ValidationMessage validationMessage : result.getErrors()) {
				writer.write(validationMessage.severity() + " "
						+ validationMessage.formattedText());
			}
		}
		writer.close();
	}

	public static void setOutputLevel(int newLevel) {
		level = newLevel;
	}

	public static void showCopyright() {
		System.out.println(COPYRIGHT);
	}

	public static void debug(String message) {
		if (level >= LEVEL_DEBUG) {
			System.out.println("   DEBUG: " + message);
		}
	}

	public static void error(Throwable e) {
		e.printStackTrace();
	}

	public static void main(String[] args) throws Exception {
		showCopyright();

		JATTChain chain = new JATTChain();

		StringHolder useConfig = new StringHolder();
		StringHolder rootDir = new StringHolder(System.getProperty("user.dir"));
		BooleanHolder collectHolder = new BooleanHolder(false);

		ArgParser parser = new ArgParser(
				"java de.guidoludwig.jatt.JATT [options]");
		parser.addOption("-c  %s #configuration to use. Available options : "
				+ CMDLINE_CONFIG_INFO + ", " + CMDLINE_CONFIG_DEFAULT + ", "
				+ CMDLINE_CONFIG_CLEAN, useConfig);
		parser.addOption(
				"-d  %s #Root Directory. Defaults to current working Directory if not set",
				rootDir);
		parser.addOption(
				"-collect %v #Flag if artwork shall be collected in subfolder called 'artwork'",
				collectHolder);

		parser.matchAllArgs(args);
		System.out.println(parser.getHelpMessage());

		if (useConfig.value == null || rootDir.value == null) {
			System.exit(0);
		}
		
		boolean collect = collectHolder.value;
		JATTConfig config = null;
		switch (useConfig.value) {
		case CMDLINE_CONFIG_INFO:
			config = new JATTConfigInfo();
			break;
		case CMDLINE_CONFIG_DEFAULT:
			config = new JATTConfigDefault(collect);
			break;
		case CMDLINE_CONFIG_CLEAN:
			config = new JATTConfigClean();
			break;
		default:
			throw new IllegalArgumentException("Unknown config : "
					+ useConfig.value);
			
		}
		if (rootDir.value == null || rootDir.value.length() == 0) {
			throw new IllegalArgumentException(
					"Working Directory must be given");
		}

		config.applyToChain(chain);

		File baseDirectory = new File(rootDir.value);
		File collectArtworkDir = null;
		if (collect) {
			collectArtworkDir = new File(baseDirectory, "artwork");
			if (!collectArtworkDir.exists()) {
				collectArtworkDir.mkdir();
			}
		}
		int startIndex = baseDirectory.getAbsolutePath().length() + 1;
		System.out.println("Scanning directories: "
				+ baseDirectory.getAbsolutePath());
		List<File> directories = addSubdirs(baseDirectory);
		JATT jatt = new JATT(directories.size(), config);
		for (File file : directories) {
			int dirNLenght = file.getAbsolutePath().length();
			String message = dirNLenght < startIndex ? "" : file
					.getAbsolutePath().substring(startIndex);
			jatt.progress(message);
			JATTContext context = new JATTContext(file, collectArtworkDir);
			chain.execute(context);
			jatt.result.addAllFrom(context.getValidationResult());
		}

		jatt.showResult();
		jatt.writeResult(baseDirectory);

	}

	private static List<File> addSubdirs(File baseDir) {
		if (!baseDir.isDirectory()) {
			throw new IllegalArgumentException(baseDir.getAbsolutePath()
					+ " is not a directory");
		}
		List<File> directories = new ArrayList<File>();
		directories.add(baseDir);
		for (String file : baseDir.list(DirectoryFilter.INSTANCE)) {
			directories.addAll(addSubdirs(new File(baseDir.getAbsolutePath()
					+ File.separatorChar + file)));
		}
		return directories;
	}

}
